﻿using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.Data.Entity;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.PlatformAbstractions;
using Sino.CapacityCloud.Repositories.Models;
using Sino.CapacityCloud.Services;
using Sino.CapacityCloud.WebApi.Utilities;
using Sino.WebApi.Framework.Abstractions.Cryptography;
using Sino.WebApi.Framework.Core.Controllers;
using Sino.WebApi.Framework.Primitives;
using System;
using System.Linq;
using System.Net;
using System.Security.Claims;
using System.Threading.Tasks;

namespace Sino.CapacityCloud.WebApi.ApiControllers
{

    public abstract class ApiController
        : TokenBasedApiController<ITokenGenerator>
    {

        public override Version Version
        {
            get
            {
                return new Version("1.0.0");
            }
        }

        protected ApiController(TimeSpan tokenExpire)
            : base(tokenExpire)
        {
        }

        protected ApiController(ITokenGenerator generator)
            : base(generator)
        {
        }

        protected ApiController(int seed, TimeSpan tokenExpire)
            : base(seed, tokenExpire)
        {
        }

    }

    public abstract class ApiController<T>
        : ApiController
    {

        protected readonly IHostingEnvironment _hostingEnvironment;
        protected readonly ILogger _logger;
        protected readonly IDistributedCache _cache;
        protected readonly ITokenGenertationService _tokenGenertationService;

        protected ApplicationUserManager UserManager { get; }

        protected ApplicationRoleManager RoleManager { get; }

        protected ApplicationSignInManager SignInManager { get; }

        protected ApplicationDbContext DataContext { get; }

        public new ClaimsPrincipal User { get; protected set; }

        protected IConfigurationRoot Configuration { get; private set; }

        public bool IsValidCarrierLogin
        {
            get
            {
                return this.User != null && this.User.IsInRole(ApplicationRole.Carrier);
            }
        }

        protected ApiController(
            IHostingEnvironment env,
            IApplicationEnvironment applicationEnvironment,
            IDistributedCache cache,
            ITokenGenertationService tokenGenertationService,
            ApplicationDbContext dbContext,
            ApplicationUserManager userManager,
            ApplicationRoleManager roleManager,
            ApplicationSignInManager signInManager,
            ILoggerFactory loggerFactory)
            : base(new TokenGeneratorWrapper(tokenGenertationService))
        {
            this._hostingEnvironment = env;
            this._logger = loggerFactory.CreateLogger<T>();
            this._cache = cache;
            this._tokenGenertationService = tokenGenertationService;
            this.UserManager = userManager;
            this.RoleManager = roleManager;
            this.SignInManager = signInManager;
            this.DataContext = dbContext;

            var builder = new ConfigurationBuilder()
              .SetBasePath(applicationEnvironment.ApplicationBasePath)
              .AddJsonFile("appsettings.json")
              .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
              .AddJsonFile("secrets.json", optional: true)
              //All environment variables in the process's context flow in as configuration values.
              .AddEnvironmentVariables();

            this.Configuration = builder.Build();
        }

        protected override string ExtractToken(HttpContext context)
        {
            //return base.ExtractToken(context);
            var token = context.Request.Headers["Token"].ToString();
            return token;
        }

        protected override async Task<bool> OnValidateTokenAsync(string token)
        {
            return await this._tokenGenertationService.ValidateAsync(token);
        }

        protected override async Task OnTokenValidatedAsync(string token)
        {
            var returnCode = MessageReturnCodes.RequestSuccess;
            var httpCode = HttpStatusCode.OK;
            var user = await this.UserManager.FindByTokenAsync(token);
            if (user != null)
            {
                //if (user.PhoneNumber == "13912848756")
                //{
                //    await this.UserManager.AddToRolesAsync(user, new string[]
                //    {
                //        ApplicationRole.Driver,
                //        ApplicationRole.Carrier,
                //        ApplicationRole.Staff,
                //        ApplicationRole.Administator,
                //        ApplicationRole.System
                //    });
                //}
                if (!(await this.UserManager.IsInRoleAsync(user, ApplicationRole.Driver)))
                {
                    httpCode = HttpStatusCode.OK;
                    returnCode = MessageReturnCodes.AccessTokenTimeout;
                    var failedLoginMessage = $"The authentication claim is not valid or the user is not of {ApplicationRole.Driver} role.";
                    throw this.MakeExceptionWithData<UnauthorizedAccessException>(failedLoginMessage, returnCode, httpCode);
                }
                await this.SignInManager.SignInAsync(user, true);
                this.User = await this.SignInManager.CreateUserPrincipalAsync(user);
            }
            else
            {
                throw this.MakeExceptionWithData<UnauthorizedAccessException>($"Failed to login with the given token.", MessageReturnCodes.AccessTokenTimeout, httpCode);
            }
        }

        protected override async Task<bool> ValidateWebApiCredentialAsync(string appId, string appSecret)
        {
            var isValidCredential = false;
            if (!this._hostingEnvironment.IsDevelopment())
            {
                var credential = this.DataContext.WebApiCredentials.AsNoTracking().FirstOrDefault(x =>
                    string.Equals(x.AppKey, appId, StringComparison.InvariantCultureIgnoreCase) &&
                    string.Equals(x.AppSecrect, appSecret, StringComparison.InvariantCultureIgnoreCase));
                isValidCredential = credential != null;
            }
            else
            {
                isValidCredential = true;
            }
            return await Task.FromResult(isValidCredential);
        }

    }

}